BiblioAdmin model binding 1 op 1
In deze les leren we hoe je het model en de view aanpast voor een property die een foreign-key is. Uitleg hierover vind je op Relationships (10/27/2016). Een praktisch voorbeeld: Saineshwar Bageri, Binding Dropdown List With Database In ASP.NET Core MVC, Dec 12 2016
Video
Probleem
- In de tabel
Orderzitten er een kolommen met een foreign-key, namelijk:CustomerIdverwijst naar deCustomertabelShippingMethodIdverwijst naar de ShippingMethod tabelOrderStatusIdverwijst naar de OrderStatus tabel
- In de tabel
OrderItemzitten er ook een kolommen met een foreign-key, namelijk:OrderIdverwijst naar de Order tabelBookIdverwijst naar de Book tabel
Oplossing
Als voorbeeld nemen we Order.
- Wat gaat hieraan vooraf:
- Je hebt de CRUD pagina's van vorige les gemaakt.
- Als je daarmee nog problemen hebt, vind je een voorbeeldoplossing op mijn Bitbucket.
- Je hebt data ingevoerd in de tabel
ShippingMethod,OrderStatusenBook. - We beginnen met onszelf of een fictieve klant toe te voegen (gebruik daarvoor de InsertingOne pagina die we vorige week gemaakt hebben):
InsertingOne Customer invoerformulier - We voegen een order toe zonder de foreign key waarden te tonen:
- We voegen data annotation toe om een kalender te tonen in het formulier voor de datums:
[DataType(DataType.Date)] [Column(TypeName = "datetime")] public DateTime OrderDate { get; set; } [DataType(DataType.Date)] [Column(TypeName = "datetime")] public DateTime ShippingDate { get; set; }
- We runnen de web app en vullen het orderformulier in:
order fk id letterlijk invoeren Met dit als resultaat:
order fk id letterlijk getoond
- We voegen data annotation toe om een kalender te tonen in het formulier voor de datums:
- In de
Order/ReadingOneRazor Page willen we de namen vanCustomer,OrderStatusenShippingMethodtonen en niet deId:- We passen de Bll/model klasse van
Orderaan. We voegen drie properties toe om de namen van de klanten, verzendmethoden en orderstatus bij te houden. Let op het gebruik van de data annotatie [NotMapped] om aan te geven dat die niet gebonden moeten worden aan het model wanneer deze waarden gepost worden.using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace BiblioAdmin.Bll { public partial class Order { [DataType(DataType.Date)] [Column(TypeName = "datetime")] public DateTime OrderDate { get; set; } [DataType(DataType.Date)] [Column(TypeName = "datetime")] public DateTime ShippingDate { get; set; } [Column(TypeName = "varchar(512)")] public string Comment { get; set; } [Column(TypeName = "int(11)")] public int Id { get; set; } [Column(TypeName = "int(11)")] public int CustomerId { get; set; } [Column(TypeName = "int(11)")] public int ShippingMethodId { get; set; } [Column(TypeName = "int(11)")] public int StatusId { get; set; } [NotMapped] [ForeignKey("CustomerId")] public Customer Customer { get; set; } [NotMapped] [ForeignKey("ShippingMethodId")] public ShippingMethod ShippingMethod { get; set; } [NotMapped] [ForeignKey("StatusId")] public OrderStatus Status { get; set; } } } - De namen kunnen Latijnse karakters bevatten zoals é, è enz. Om die correct uit de database in te lezen moeten we het model van
Countryaanpassen. Daarvoor heb je deMySql.Data.EntityFrameworkCore.DataAnnotationsnamespace nodig (MySQL Configuring Character Sets and Collations in EF Core):[MySqlCharset("latin1")] [NotMapped] [ForeignKey("CustomerId")] public Customer Customer { get; set; } - In de
Ongetmethode van deReadingklasse in het Order/ReadingOne.cshtml.cs bestand, halen we de rijnen van deOneModelOrderStatus,OrderShippingenCustomertabel op en zetten ze in de overeenkomstige properties:public void OnGet(int? id) { this.Order = dbContext.Order.SingleOrDefault(m => m.Id == id); Order.ShippingMethod = dbContext.ShippingMethod.SingleOrDefault(m => m.Id == this.Order.ShippingMethodId); Order.Status = dbContext.OrderStatus.SingleOrDefault(m => m.Id == this.Order.StatusId); Order.Customer = dbContext.Customer.SingleOrDefault(m => m.Id == this.Order.CustomerId); OrderList = dbContext.Order.ToList(); } - In de
ReadingOneview van Order vangen we het model op en tonen de naam van deShippingMethod,OrderStatusenCustomerin de HTML:<div> <label for="Order-Name">Klant</label> <input id="Order-Name" name="Order-Name" type="text" value="@Model.Order.Customer.FirstName @Model.Order.Customer.LastName" readonly> </div> <div> <label for="Order-Name">Verzendingsmethode</label> <input id="Order-Name" name="Order-Name" type="text" value="@Model.Order.ShippingMethod.Name" readonly> </div> <div> <label for="Order-Name">Status</label> <input id="Order-Name" name="Order-Name" type="text" value="@Model.Order.Status.Name" readonly> </div> - Met dit als resultaat:
order fk namen getoond
- We passen de Bll/model klasse van
- Partial Page
Zoals je hierboven ziet, moeten we de id's in de lijst onderaan nog vervangen door de respectievelijke namen. Maar we hebben die lijst op elke pagina van Order staan. I.p.v. elke pagina te wijzigen gaan we een nieuwe techniek leren, nameijk Partial Pages.- Maak een map met de naam Order in de Pages/Shared map.
- Maak daarin een Razor Page met de naam _ReadingAll.cshtml.
- Verwijder de
@Pagedirective omdat dit geen volwaardige Razor Page is en voeg een model toe van het type generieke lijst met instanties van de BllOrderklasse:---@Page---model List<Bll.Order> @{ } - Verplaats de volgende html uit de Razor pages met de naam /Order/
ReadingOne.cshtml, /Order/Index.cshtml, /Order/UpdatingOne.cshtml en /Order/InsertingOne.cshtml:en plaats die in /Shared/Order/_ReadingAll.cshtml:<aside class="list"> <table class="list"> <thead> <tr> <th> </th> <th> Id </th> <th> Besteldatum </th> <th> Verzenddatum </th> <th> Opmerking </th> <th> Klant </th> <th> Verzedingsmethode </th> <th> Status </th> </tr> </thead> <tbody> @foreach (var item in Model) { <tr> <td> <a href="Order/ReadingOne/@item.Id">Select</a> | </td> <td> @item.Id </td> <td> @item.OrderDate </td> <td> @item.ShippingDate </td> <td> @item.Comment </td> <td> @item.CustomerId </td> <td> @item.ShippingMethodId </td> <td> @item.StatusId </td> </tr> } </tbody> </table> </aside> - In de de Razor pages met de naam /Order/
ReadingOne.cshtml, /Order/Index.cshtml, /Order/UpdatingOne.cshtml en /Order/InsertingOne.cshtml:en plaats die in /Shared/Order/_ReadingAll.cshtml vervang je de html door de Partial taghelper en geef je de generieke lijstOrderListmee door:@page @model BiblioAdmin.Pages.Order.IndexModel @{ } <h1>Order Index</h1> <a asp-page="./InsertingOne" asp-page-handler="Inserting">Inserting One </a> <partial name="Order/_ReadingAll" model="Model.OrderList"/>
- In de
InsertingOneview willen we de lijst van de verzendmethoden, orderstus en klanten tonen een keuzelijst:- In de
Ongetmethode van deInsertingOneklasse in het Order/InsertingOne.cshtml.cs bestand lezen we verzendmethoden, orderstus en klanten in en geven die aan de view door met behulp van publieke properties:Modelusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace BiblioAdmin.Pages.Order { public class InsertingOneModel : PageModel { private readonly Bll.Docent2Context dbContext; // voeg constructor toe om geïnjecteerde DBContext // te kunnen binnenkrijgen in deze klasse public InsertingOneModel(Bll.Docent2Context dbContext) { this.dbContext = dbContext; } [BindProperty] public Bll.Order Order { get; set; } public List<Bll.Order> OrderList { get; set; } public List<Bll.Customer> CustomerList { get; set; } public List<Bll.OrderStatus> OrderStatusList { get; set; } public List<Bll.ShippingMethod> ShippingMethodList { get; set; } public void OnGet() { OrderList = dbContext.Order.ToList(); CustomerList = dbContext.Customer.ToList(); OrderStatusList = dbContext.OrderStatus.ToList(); ShippingMethodList = dbContext.ShippingMethod.ToList(); } public ActionResult OnPostInsert(Bll.Order order) { if (!ModelState.IsValid) { // als er een foutief gegeven is ingetypt ga terug // de pagina en toon de fout return Page(); // return page, nog een nieuwe ingeven } // dbContext.Order.Update(order); dbContext.Order.Add(order); dbContext.SaveChanges(); // keer terug naar de index pagina van Order return RedirectToPage("Index"); } } } - We passen de
Order/InsertingOneview aan en lezen deoptionelementen uit deShippingMethodList,OrderStatusListenCustomerList:@page @model BiblioAdmin.Pages.Order.InsertingOneModel @{ } <h3>InsertingOne Order</h3> <hr /> <br /> <form method="post"> <fieldset> <div> <label asp-for="Order.OrderDate"></label> <input asp-for="Order.OrderDate" /> </div> <div> <label asp-for="Order.ShippingDate"></label> <input asp-for="Order.ShippingDate" /> </div> <div> <label asp-for="Order.Comment"></label> <textarea asp-for="Order.Comment"></textarea> </div> <div> <label asp-for="Order.CustomerId"></label><input asp-for="Order.CustomerId" /><select id="Order_CustomerId" name="Order.CustomerId"> @foreach (var item in Model.CustomerList) { <option value="@item.Id">@item.FirstName @item.LastName</option> } </select> </div> <div> <label asp-for="Order.ShippingMethodId"></label><input asp-for="Order.ShippingMethodId" /><select id="Order_ShippingMethodId" name="Order.ShippingMethodId"> @foreach (var item in Model.ShippingMethodList) { <option value="@item.Id">@item.Name</option> } </select> </div> <div> <label asp-for="Order.StatusId"></label><input asp-for="Order.StatusId" /><select id="Order_StatusId" name="Order.StatusId"> @foreach (var item in Model.OrderStatusList) { <option value="@item.Id">@item.Name</option> } </select> </div> </fieldset> <div> <button type="submit" value="Insert" asp-page-handler="Insert">Insert</button> </div> </form> <partial name="Order/_ReadingAll" model="Model.OrderList" />
- In de
- In de
Order/UpdatingOneview moeten we de keuzelijst ook opvullen met elementen uit deShippingMethodList,OrderStatusListenCustomerList:
. Maar er komt iets extra bij: in het keuzeveld moet de naam van de ingegeven verzendmehode, klant en orderstatus komen te staan:- In de
Ongetmethode van deUpdatingOneklasse in het Order/UpdatingOne.cshtml.cs bestand lezen we verzendmethoden, orderstus en klanten in en geven die aan de view door met behulp van publieke properties:Modelusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; namespace BiblioAdmin.Pages.Order { public class UpdatingOneModel : PageModel { private readonly Bll.Docent2Context dbContext; // voeg constructor toe om geïnjecteerde DBContext // te kunnen binnenkrijgen in deze klasse public UpdatingOneModel(Bll.Docent2Context dbContext) { this.dbContext = dbContext; } [BindProperty] public Bll.Order Order { get; set; } public List<Bll.Order> OrderList { get; set; } public List<Bll.Customer> CustomerList { get; set; } public List<Bll.OrderStatus> OrderStatusList { get; set; } public List<Bll.ShippingMethod> ShippingMethodList { get; set; } public void OnGet(int? id) { this.Order = dbContext.Order.SingleOrDefault(m => m.Id == id); Order.ShippingMethod = dbContext.ShippingMethod.SingleOrDefault(m => m.Id == this.Order.ShippingMethodId); Order.Status = dbContext.OrderStatus.SingleOrDefault(m => m.Id == this.Order.StatusId); Order.Customer = dbContext.Customer.SingleOrDefault(m => m.Id == this.Order.CustomerId); OrderList = dbContext.Order.ToList(); CustomerList = dbContext.Customer.ToList(); OrderStatusList = dbContext.OrderStatus.ToList(); ShippingMethodList = dbContext.ShippingMethod.ToList(); } public ActionResult OnPostUpdate(Bll.Order order) { if (!ModelState.IsValid) { // als er een foutief gegeven is ingetypt ga terug // de pagina en toon de fout return Page(); // return page } // dbContext.OrderStatus.Update(orderStatus); dbContext.Order.Update(order); dbContext.SaveChanges(); // keer terug naar de index pagina van OrderStatus return RedirectToPage("Index"); } } } - We passen vervolgens de
UpdatingOneview aan:@page "{id}" @model BiblioAdmin.Pages.Order.UpdatingOneModel @{ } <h3>UpdatingOne Order</h3> <hr /> <br /> <form method="post"> <fieldset> <div> <label asp-for="Order.Id"></label> <input asp-for="Order.Id" readonly /> </div> <div> <label asp-for="Order.OrderDate"></label> <input asp-for="Order.OrderDate" /> </div> <div> <label asp-for="Order.ShippingDate"></label> <input asp-for="Order.ShippingDate" /> </div> <div> <label asp-for="Order.Comment"></label> <textarea asp-for="Order.Comment"></textarea> </div> <div> <label asp-for="Order.CustomerId"></label><input asp-for="Order.CustomerId" /><select id="Order-CustomerId" name="Order.CustomerId"> @foreach (var item in Model.CustomerList) { <option value="@item.Id" selected="@(item.Id == Model.Order.CustomerId ? true : false)"> @item.FirstName @item.LastName </option> } </select> </div> <div> <label asp-for="Order.ShippingMethodId"></label><input asp-for="Order.ShippingMethodId" /><select id="Order-ShippingMethodId" name="Order.ShippingMethodId"> @foreach (var item in Model.ShippingMethodList) { <option value="@item.Id" selected="@(item.Id == Model.Order.ShippingMethodId ? true : false)"> @item.Name </option> } </select> </div> <div> <label asp-for="Order.StatusId"></label><input asp-for="Order.StatusId" /><select id="Order-StatusId" name="Order.StatusId"> @foreach (var item in Model.OrderStatusList) { <option value="@item.Id" selected="@(item.Id == Model.Order.StatusId ? true : false)"> @item.Name </option> } </select> </div> </fieldset> <div> <button type="submit" value="Update" asp-page-handler="Update">Update</button> </div> </form> <partial name="Order/_ReadingAll" model="Model.OrderList" />
- In de
Opdracht
- Maak zelf de 1 op1 voor OrderStatus, ShippingMethode en Customer zoals in het lesvoorbeeld.
- Maak een Partial page voor ReadingAll voor alle entiteiten, nl Order, Customer, OrderItem, ShippingMethod, Customer, OrderStatus en Book.
- Zorg ervoor de de 1 op 1 in tussen OrderItem en Book zo implementeert dat als je een orderitem toevoegt, van een lijst kan selecteren uit boeken.
- De foreign key voor OrderId geef je letterlijk in met de Id van de order waaraan je de OderItem wilt koppelen. In de volgende les leren we de 1 op n relatie tussen Order en OrderItem implemeren.
- Maak een lay-out pagina voor de Admin pagina's. Als voorbeeld van de lay-out verwijs ik naar: Fric-frac Wireframes Person.
- Stel enkele eenvoudige CSS regels op om de lay-out van de Admin pagina's gebruiksvriendelijk te maken. Maak gebruik van GRID en Flexbox om de hoofdlay-out van de pagina's te maken. Info over flex en grid:
- Deze opdracht maakt deel uit van de eindopdracht.
2020-12-13 19:22:29